package com.java.compilation;

import com.java.compilation.helper.Keyword;
import com.java.compilation.helper.TokenType;

import java.io.*;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

public class JackTokenizer {

    private BufferedReader reader;
    private String statement;
    private String token;
    private TokenType tokenType;
    private Keyword keyword;
    private String symbol;
    private static final Set<String> symbols = new HashSet<>();
    private String stringVal;
    private int intVal;
    private String identifier;
    private boolean isCommentHalf;

    public JackTokenizer(File jackFile) {
        try {
            reader = new BufferedReader(new FileReader(jackFile));
            initSymbols();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    private static void initSymbols() {
        symbols.addAll(Arrays.asList("~", "{", "}", "(", ")", "[", "]", ".", ",", ";", "+", "-", "*", "/", "&", "|", "<", ">", "=", "-"));
    }

    public boolean hasMoreTokens() throws IOException {
        if (isLegalStatement()) {
            return true;
        }
        while ((statement = reader.readLine()) != null) {
            if (isComment()) {
                continue;
            }

            return true;
        }
        return false;
    }

    private boolean isComment() {
        if (isCommentHalf && statement.indexOf("*/") == -1) {
            return true;
        }

        statement = statement.trim();
        // 去掉注释
        if (statement.isEmpty() || statement.startsWith("//")) {
            return true;
        }

        if (statement.startsWith("/*") || isCommentHalf) {
            isCommentHalf = false;
            if (statement.indexOf("*/") == statement.length() - 2) {
                return true;
            } else if (statement.indexOf("*/") != -1){
                statement = statement.substring(statement.indexOf("*/"));
            } else {
                isCommentHalf = true;
                return true;
            }
        }
        return false;
    }

    private boolean isLegalStatement() {
        return statement != null && !statement.isBlank() && !isComment();
    }

    public void advance() {
        // 词法分析
        for (int i = 0; i < statement.length(); i++) {
            // 字符串分词
            if (statement.charAt(0) == '"') {
                if (i == 0 || statement.charAt(i) != '"') {
                    continue;
                }
                endParse(i + 1);
                break;
            }

            // 空格
            if (statement.charAt(i) == ' '
                    || symbols.contains(String.valueOf(statement.charAt(i)))) {
                endParse(i);
                break;
            }
        }
        if (Keyword.isKeyword(token)) {
            tokenType = TokenType.KEYWORD;
            keyword = Keyword.keyword(token);
        } else if (symbols.contains(token)) {
            tokenType = TokenType.SYMBOL;
            symbol = token;
        } else if (token.startsWith("\"") && token.endsWith("\"")) {
            tokenType = TokenType.STRING_CONST;
            stringVal = token.substring(1, token.length() - 1);
        } else if (isIntValue(token)) {
            tokenType = TokenType.INT_CONST;
            intVal = Integer.parseInt(token);
        } else {
            tokenType = TokenType.IDENTIFIER;
            identifier = token;
        }
    }

    private boolean isIntValue(String token) {
        try {
            int i = Integer.parseInt(token);
            return i >= 0 && i <= 32767;
        } catch (Exception e) {
            return false;
        }
    }

    public void endParse(int endIndex) {
        if (endIndex == 0) {
            endIndex++;
        }
        this.token = statement.substring(0, endIndex);
        this.statement = statement.substring(endIndex).trim();
    }

    public TokenType tokenType() {
        return tokenType;
    }

    public Keyword keyword() {
        return keyword;
    }

    public String symbol() {
        return symbol;
    }

    public String identifier() {
        return identifier;
    }

    public int intVal() {
        return intVal;
    }

    public String stringVal() {
        return stringVal;
    }

}
